package edu.zju.gis.dbfg.fileserver.dataLoader;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;

import edu.zju.gis.dbfg.common.constant.Seprator;
import edu.zju.gis.dbfg.common.exception.LoaderException;
import edu.zju.gis.dbfg.fileserver.model.ConstructedDataField;
import edu.zju.gis.dbfg.fileserver.model.ConstructedDataset;
import edu.zju.gis.dbfg.fileserver.model.LoaderMonitor;
import edu.zju.gis.dbfg.fileserver.service.LoaderMonitorService;
import edu.zju.gis.dbfg.fileserver.util.BeanUtil;
import edu.zju.gis.dbfg.fileserver.util.DateUtil;
import edu.zju.gis.dbfg.fileserver.util.reader.DataReader;
import edu.zju.gis.dbfg.fileserver.util.reader.ShpFileReader;
import edu.zju.gis.dbfg.fileserver.util.reader.WktFileReader;
import lombok.Getter;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * 数据装载类的基类
 */
@Getter
@Setter
public abstract class Loader implements LoadProcedure, Runnable {

    private static final Logger logger = LoggerFactory.getLogger(Loader.class);

    protected ConstructedDataset dataset; // 数据集元数据定义

    // 定义用户指定字段与读入数据字段的映射关系，String为dataset定义的字段，Integer为该字段在读入数据列中的第几位
    protected Map<String, Integer> header;

    protected DataReader source;

    protected String filename;

    protected Boolean hasHeader = true;
    protected Seprator seprator = Seprator.COMMA;

    protected int total;
    protected int success;
    // 用来记录错误tuple以及错误原因
    protected List<String> errors;
    protected LoaderMonitorService monitorService;
    protected LoaderMonitor monitor;

    /**
     * 校验Loader参数设置
     */
    @Override
    public void check() {
        if (filename == null) {
//            throw new LoaderException("Dataset filename needs to be defined");
        }
        if (dataset == null) {
//            throw new LoaderException("Dataset metadata needs to be defined");
        }
        if (dataset.getId() == null) {
//            throw new LoaderException("Dataset id needs to be defined");
        }
        if (dataset.getName() == null) {
//            throw new LoaderException("Dataset name needs to be defined");
        }
        if (this.header == null) {
//            throw new LoaderException("Data header should be defined");
        }
    }

    public void init() {
        this.monitorService = BeanUtil.getBean(LoaderMonitorService.class);
//        this.monitor = new LoaderMonitor();
    }

    /**
     * 设置装载数据源
     */
    @Override
    public void setSource() {
        filename = filename.replace("file://", "");
        if (this.filename.endsWith("tsv") || this.filename.endsWith("csv") || this.filename.endsWith("txt") || !this.filename.contains(".")) {
            this.source = new WktFileReader(seprator, hasHeader);
            this.source.filename(filename);
            this.source.init();
        } else if (this.filename.endsWith("shp")) {
            this.source = new ShpFileReader();
            this.source.filename(filename);
            this.source.init();
        } else {
            throw new LoaderException("Unvalid file: " + this.filename);
        }
    }

    /**
     * 将读入的单行数据转换为具有字段信息的数据
     *
     * @param lines
     * @return
     */
    @Override
    public abstract List<Map<ConstructedDataField, String>> mapValues(List<String> lines) throws IOException;

    /**
     * 批量校验数据的规范性
     *
     * @param values
     * @return
     */
    @Override
    public List<Map<ConstructedDataField, String>> batchValidate(List<Map<ConstructedDataField, String>> values) {
        List<Map<ConstructedDataField, String>> result = new ArrayList<>();
        for (Map<ConstructedDataField, String> o : values) {
            if (this.validate(o))
                result.add(o);
        }
        return result;
    }

    /**
     * 校验数据的规范性
     *
     * @param values
     * @return
     */
    @Override
    public abstract Boolean validate(Map<ConstructedDataField, String> values);

    /**
     * 对于不同后端，数据装载过程自定义实现
     */
    public abstract void load(Map<ConstructedDataField, String> value);

    /**
     * 批量装载
     * 对于不同存储后端，批量装载过程自定义实现
     */
    public abstract int batchLoad(List<Map<ConstructedDataField, String>> values);


    @Override
    public void exec() throws IOException {
        check();
        init();
        initMonitor();
        // TODO 默认step size是1000
        List<String> values = source.next(1000);
        while (values != null && values.size() > 0) {
            List<Map<ConstructedDataField, String>> data = batchValidate(mapValues(values));
            success += batchLoad(data);
            // 更新任务状态
            uploadMonitor();
            values = source.next(1000);
        }
        finish();
        logger.info("Storage Process finished");
        logger.info(String.format("Total: %d, Success: %d", this.total, this.success));
        closeMonitor();
    }

    /**
     * 获得当前dataset的字段信息
     *
     * @return
     */
    protected ArrayList<ConstructedDataField> getMetaFields() {
        String metaFieldsStr = dataset.getFields();
        JsonParser parser = new JsonParser();
        JsonArray jsonArray = parser.parse(metaFieldsStr).getAsJsonArray();
        Gson gson = new Gson();
        ArrayList<ConstructedDataField> metaFields = new ArrayList<>();
        //遍历JsonArray
        for (JsonElement field : jsonArray) {
            //使用Gson，直接转成Bean对象
            ConstructedDataField ConstructedDataField = gson.fromJson(field, ConstructedDataField.class);
            metaFields.add(ConstructedDataField);
        }
        return metaFields;
    }

    protected void initMonitor() {
//        this.monitor.setId(UUID.randomUUID().toString());
//        this.monitor.setDatasetname(this.dataset.getName());
//        this.monitor.setCreatetime(DateUtil.now());
//        this.monitor.setErrornumb(0);
//        this.monitor.setSuccessnumb(0);
//        this.monitor.setStatus("Init");
//        this.monitor.setLogs("Start");
//        this.monitor.setUserId(this.dataset.getUserid());
//        monitorService.save(this.monitor);
    }

    protected void uploadMonitor() {
        StringBuilder logsBuilder = new StringBuilder();
        if (errors != null) {
            for (String error : errors)
                logsBuilder.append(error);
        }
        this.monitor.setSuccessnumb(this.total);
        this.monitor.setSuccessnumb(this.success);
        this.monitor.setErrornumb(this.total - this.success);
        this.monitor.setStatus("Loading");
        //TODO 日志过长
        if (logsBuilder.toString().length() > 20000)
            this.monitor.setLogs(logsBuilder.toString().substring(0, 20000) + "...");
        else
            this.monitor.setLogs(logsBuilder.toString().trim());
        this.monitorService.update(this.monitor);
    }

    protected void closeMonitor() {
        this.monitor.setStatus("Finish");
        this.monitorService.update(this.monitor);
    }


    @Override
    public void run() {
        try {
            this.exec();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


}
